'use strict';


import {computeNormals} from './calNormal'
import {computeUVs} from './calUV'
import {calWireFrame} from "./calWireFrame";
import {calNormalVertices} from './calNormalVertices'
// import {calBoundingBoxVertices} from './calBoundingBoxVertices'
import {AABB, Sphere} from "../geometry";
import {Vec2, Vec3} from "../math";
import {IOriginVertexData} from "./originDtata";
import {IMeshData, MeshData} from "./MeshData";

const NO_VALUE = -1;

class IdxGroup {
    constructor(public idxPos = NO_VALUE,
                public idxTexCoord = NO_VALUE,
                public idxNormal = NO_VALUE){
    }
}

class ObjFace {
    idxGroups = [new IdxGroup(), new IdxGroup(), new IdxGroup()];
    constructor(v1:string, v2:string, v3:string) {
        this.parseLine(v1, this.idxGroups[0]);
        this.parseLine(v2, this.idxGroups[1]);
        this.parseLine(v3, this.idxGroups[2]);
    }

    parseLine(v:string, idxGroup:IdxGroup) {
        const tokens = v.split('/');
        const len = tokens.length;
        idxGroup.idxPos = parseInt(tokens[0]) - 1;
        if (len > 1) {
            const texCoord = tokens[1];
            idxGroup.idxTexCoord = texCoord.length > 0 ? parseInt(texCoord) - 1 : NO_VALUE;
            if (len > 2) {
                idxGroup.idxNormal = tokens[2].length > 0 ? parseInt(tokens[2]) - 1 : NO_VALUE;
            }
        }
    }
}

function reorderList(vertices:Vec3[], texCoords:Vec2[], normals:Vec3[], faces:ObjFace[]) :IOriginVertexData{
    let indicesArray:number[] = [];
    let posArray = [];
    for (let i = 0, ilen = vertices.length; i < ilen; ++i) {
        posArray.push(vertices[i].x, vertices[i].y, vertices[i].z);
    }
    let texCoordArray = new Array(vertices.length * 2);
    let normalArray = new Array(vertices.length * 3);
    for (let i = 0, ilen = faces.length; i < ilen; ++i) {
        let idxGroups = faces[i].idxGroups;
        processFaceVertex(idxGroups[0], texCoords, texCoordArray, normals, normalArray, indicesArray);
        processFaceVertex(idxGroups[1], texCoords, texCoordArray, normals, normalArray, indicesArray);
        processFaceVertex(idxGroups[2], texCoords, texCoordArray, normals, normalArray, indicesArray);
    }
    return {
        positions: posArray,
        normals: normalArray,
        texCoords: texCoordArray,
        indices: indicesArray,
    };
}

function processFaceVertex(idxGroup:IdxGroup, texCoords:Vec2[], texCoordArray:number[], normals:Vec3[], normalArray:number[], indicesArray:number[]) {
    let posIndex = idxGroup.idxPos;
    indicesArray.push(posIndex);

    // 如果有纹理
    if (idxGroup.idxTexCoord >= 0) {
        texCoordArray[2 * posIndex] = texCoords[idxGroup.idxTexCoord].x;
        texCoordArray[2 * posIndex + 1] = 1 - texCoords[idxGroup.idxTexCoord].y;  // 翻转Y坐标
    } else {
        texCoordArray[2 * posIndex] = 0;
        texCoordArray[2 * posIndex + 1] = 0;
    }

    if (idxGroup.idxNormal >= 0) {
        normalArray[3 * posIndex] = normals[idxGroup.idxNormal].x;
        normalArray[3 * posIndex + 1] = normals[idxGroup.idxNormal].y;
        normalArray[3 * posIndex + 2] = normals[idxGroup.idxNormal].z;
    }
}


/**
 *
 * @param objdata
 * @return {
 *      positions:[]
 *      indices:[],
 *      normals:[],
 *      texCoords:[],
 *      sphere: {},
 *      wireFrame:{
 *          positions:positions,
 *          indices:[]
 *      },
 *      norm:{
 *          positions:[]
 *      },
 *      aabb:{
 *          positions:[],
 *          indices:[]
 *      }
 * }
 */
export function parseObj(objdata:string):IMeshData{
    let lines = objdata.split('\n');

    let vertices = [];
    let texCoords = [];
    let normals = [];
    let faces = [];

    let hasTexture = false;
    let hasNormal = false;
    for (let i = 0, ilen = lines.length; i < ilen; ++i) {
        let tokens = lines[i].split(/\s+/g);
        switch (tokens[0]) {
            case 'v':
                vertices.push(new Vec3(
                    parseFloat(tokens[1]),
                    parseFloat(tokens[2]),
                    parseFloat(tokens[3])
                ));
                break;
            case 'vt':
                texCoords.push(new Vec2(
                    parseFloat(tokens[1]),
                    parseFloat(tokens[2])
                ));
                if (!hasTexture){
                    hasTexture = true;
                }
                break;
            case 'vn':
                normals.push(new Vec3(
                    parseFloat(tokens[1]),
                    parseFloat(tokens[2]),
                    parseFloat(tokens[3])
                ));
                if(!hasNormal){
                    hasNormal = true;
                }
                break;
            case 'f':
                faces.push(new ObjFace(tokens[1], tokens[2], tokens[3]));
                break;
            default:
                // ignore
                break;
        }
    }

    let output =  reorderList(vertices, texCoords, normals, faces);

    if (!hasNormal){
        computeNormals(output);
    }

    if (!hasTexture){
        computeUVs(output)
    }

    let aabb = new AABB();
    let min = new Vec3();
    let max = new Vec3();
    AABB.fromPointArray(aabb,output.positions);
    aabb.getBoundary(min,max);

    let size = new Vec3();
    Vec3.multiplyScalar(size,aabb.halfExtents,2);
    let d = 0.1 * (size.x + size.y + size.z) / 3;

    let sphere = new Sphere();
    AABB.toBoundingSphere(sphere,aabb);

    // 生成 渲染wireframe的索引数据
    let originWireFrameData = calWireFrame(output);

    // 生成  渲染normal的顶点数据
    let originNormalVertexData = calNormalVertices(output,d);

    // 生成 渲染aabb的顶点数据
    // TODO: sphere data
    // let aabbData = calBoundingBoxVertices(min,max);

    // 直接根据 output生成一个可以直接渲染的数据
    return new MeshData(
        output,
        originWireFrameData,
        originNormalVertexData,
        sphere,
        aabb);
}


// test
//
// function test_ObjParse(){
//     let fs = require('fs');
//     let path = require('path');
//
//
//     let filepath = path.join(__dirname,'../../assets/monkey.obj');
//     let content = fs.readFileSync(filepath,'utf8');
//     let output = parseObj(content);
//     console.log('output.positions.size:',output.positions.length);
//     console.log('output.normals.size:',output.normals.length);
//     console.log('output.texCoords.size:',output.texCoords.length);
//     console.log('output.indices.size:',output.indices.length);
//
//     console.log('===============');
//     for (let k in output){
//         console.log(k);
//     }
// }

// console.log(output.positions);
// console.log(output.normals);
// console.log(output.texCoords);
// console.log(output.indices);
// console.log(output.sphere);
// console.log(output.wireFrame);
// console.log(output.norm);
// console.log(output.aabb);





